<template>
  <div ref="editContainer" :class="props.className" :style="'width: ' + props.width + '; height: ' + props.height + ';' + style"></div>
</template>
<script setup>
import * as monaco from 'monaco-editor';
import editorWorker from 'monaco-editor/esm/vs/editor/editor.worker?worker';
import jsonWorker from 'monaco-editor/esm/vs/language/json/json.worker?worker';
import cssWorker from 'monaco-editor/esm/vs/language/css/css.worker?worker';
import htmlWorker from 'monaco-editor/esm/vs/language/html/html.worker?worker';
import tsWorker from 'monaco-editor/esm/vs/language/typescript/ts.worker?worker';
import { h } from 'vue';

const props = defineProps({
  className: {
    // 类名
    type: String,
    default: 'code-mirror',
  },
  style: {
    // 样式
    type: Object,
    default: () => ({}),
  },
  width: {
    // 宽度
    type: String,
    default: '100%',
  },
  height: {
    // 高度
    type: String,
    default: '100%',
  },
  codevalue: {
    // 代码值
    type: String,
    default: '',
  },
  language: {
    // 代码语言
    type: String,
    default: 'javascript',
  },
  readonly: {
    // 只读
    type: Boolean,
    default: false,
  },
});

self.MonacoEnvironment = {
  getWorker(_, label) {
    if (label === 'json') {
      return new jsonWorker();
    }
    if (label === 'css' || label === 'scss' || label === 'less') {
      return new cssWorker();
    }
    if (label === 'html' || label === 'handlebars' || label === 'razor') {
      return new htmlWorker();
    }
    if (label === 'typescript' || label === 'javascript') {
      return new tsWorker();
    }
    return new editorWorker();
  },
};

const { proxy } = getCurrentInstance();

// 创建对编辑器的引用
const editContainer = ref();

// Monaco Editor 实例
let editorIde = null;

// 获取编辑器的配置选项
const options = null;

// 获取行高
const lineHeight = null;

// 声明change事件
const emit = defineEmits(['change']);

/**
 * 获取编辑器全部值
 */
const getEditorValue = () => {
  return toRaw(editorIde).getValue();
};

/**
 * 设置编辑器内容
 */
const setEditorValue = (newCodeValue) => {
  return toRaw(editorIde).setValue(newCodeValue);
};

/**
 * 监听编辑器内容
 */
const changeEditorValue = () => {
  emit('change', toRaw(editorIde).getValue(), ''); // 触发事件并传递当前值
};

// 在组件挂载后执行
onMounted(() => {
  // 创建 Monaco Editor 实例
  editorIde = monaco.editor.create(editContainer.value, {
    value: props.codevalue, // 设置初始代码值
    language: props.language, // 设置语言为自定义语言
    theme: 'vs', // 主题 官方自带三种主题vs, hc-black, or vs-dark
    renderLineHighlight: 'all', // 行亮
    selectOnLineNumbers: true, // 显示行号
    automaticLayout: true, // 自动布局
    renderSideBySide: false,
    readOnly: props.readonly, // 只读
    fontSize: 16, // 字体大小
    overviewRulerBorder: true, // 不要滚动条的边框
    scrollBeyondLastLine: false, // 取消代码后面一大段空白
    contextmenu: true, // 上下文菜单右键菜单
    codeLens: true,
    folding: true, // 是否启用代码折叠
    comments: {
      // 注释配置
      ignoreEmptyLines: true, // 插入行注释时忽略空行。默认为真。
      insertSpace: true, // 在行注释标记之后和块注释标记内插入一个空格。默认为真。
    },
    minimap: {
      // 预览图设置
      enabled: false, // 是否启用预览图
    },
    lightbulb: {
      enabled: false, // 快速修复功能
    },
  });

  // options = editorIde.getOptions()

  // lineHeight = options.get(monaco.editor.EditorOption.lineHeight)

  // 监听滚动事件
  // 1000ms 的节流间隔
  editorIde.onDidScrollChange(
    throttle((e) => {
      const layoutInfo = editorIde.getLayoutInfo();
      const scrollTop = e.scrollTop; // 当前滚动位置
      const visibleHeight = layoutInfo.height; // 可见区域的高度
      const model = editorIde.getModel();
      const lineCount = model.getLineCount(); // 总行数

      // 判断是否滑到底部
      if (scrollTop >= lineCount * lineHeight - visibleHeight) {
        emit('load-more'); // 发出 load-more 事件
      }
    }, 1000),
  );

  // 监听值的变化
  editorIde.onDidChangeModelContent((val) => {
    changeEditorValue();
  });
});

// 节流函数
function throttle(func, wait) {
  let lastTime = 0;
  return function (...args) {
    const now = Date.now();
    if (now - lastTime >= wait) {
      lastTime = now;
      func.apply(this, args);
    }
  };
}

// 监听 value 变化，加载新的文本内容
watch(
  () => props.codevalue,
  (newValue) => {
    if (editorIde) {
      const oldValue = toRaw(editorIde).getValue(); // 获取编辑器内容
      toRaw(editorIde).setValue(oldValue + newValue); // 更新编辑器内容
    }
  },
);

// 组件卸载时销毁实例
onBeforeUnmount(() => {
  if (editorIde) {
    editorIde.dispose();
  }
});

//导出获取编辑器全部值的方法，在提交表单时给后端传值
defineExpose({
  getEditorValue,
  setEditorValue,
});
</script>
